package com.law.lawcaculator.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.googlecode.aviator.AviatorEvaluator;
import com.law.lawcaculator.mapper.*;
import com.law.lawcaculator.model.dto.GenerateReportResponse;
import com.law.lawcaculator.model.entity.*;
import com.law.lawcaculator.service.ReportService;
import com.law.lawcaculator.service.RuleEngineService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.stream.Collectors;

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import java.io.OutputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jakarta.servlet.http.HttpServletResponse;

@Service
@RequiredArgsConstructor
@Slf4j
public class ReportServiceImpl implements ReportService {

    private final ReportSessionsMapper reportSessionsMapper;
    private final RuleEngineService ruleEngineService;
    private final ItemTemplateMapper itemTemplateMapper;
    private final ItemSectionMappingMapper itemSectionMappingMapper;
    private final ReportLayoutMapper reportLayoutMapper;
    private final QuestionOptionsMapper questionOptionsMapper;

    /**
     * 生成法律计算报告的实现
     * @param sessionId 会话ID，用于标识用户会话
     * @param answers 用户提交的答案列表，包含问题ID、选项ID和答案内容
     * @param appId 应用ID，将作为calculatorCode存储到数据库中，用于标识不同的应用场景
     * @return 包含报告内容的响应对象
     */
    @Override
    public GenerateReportResponse generateReport(String sessionId, List<SubmitAnswers> answers, String appId) {
        // 1. 将会话ID和答案转换为Map格式用于计算
        Map<String, Object> userData = convertAnswersToMap(answers);

        // 2. 执行规则计算
        Map<String, Object> calculationResults = ruleEngineService.executeCalculationRules(sessionId, userData);

        // 3. 生成HTML报告
        String htmlContent = generateHtmlReport(sessionId, calculationResults);

        // 4. 保存会话数据，将appId作为calculatorCode
        saveSessionData(sessionId, answers, calculationResults, htmlContent, appId);

        return new GenerateReportResponse(sessionId, htmlContent);
    }
    
    /**
     * 从HTML中提取纯文本
     */


    @Override
    public void downloadWordReport(String sessionId, HttpServletResponse response) {
        try {
            // 去除sessionId中可能存在的单引号，确保查询匹配
            if (sessionId != null) {
                sessionId = sessionId.replaceAll("^'|'$", "");
            }
            
            // 1. 从数据库获取会话数据
            ReportSessions session = reportSessionsMapper.selectOne(new LambdaQueryWrapper<ReportSessions>().eq(ReportSessions::getSessionId, sessionId));
            if (session == null || session.getHtmlContent() == null) {
                throw new RuntimeException("Report not found");
            }

            // 2. 设置响应头
            response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
            response.setHeader("Content-Disposition", "attachment; filename=report_" + sessionId + ".docx");

            // 3. 创建Word文档
            XWPFDocument document = new XWPFDocument();

            // 4. 处理HTML内容，保留格式
            processHtmlContentToWord(session.getHtmlContent(), document);

            // 5. 写入响应流
            try (OutputStream out = response.getOutputStream()) {
                document.write(out);
            }

            document.close();

        } catch (Exception e) {
            log.error("Failed to download report: {}", sessionId, e);
            throw new RuntimeException("Report download failed", e);
        }
    }
    
    /**
     * 从HTML中提取纯文本
     */
    private String extractPlainTextFromHtml(String html) {
        if (html == null) {
            return "";
        }
        // 简单的HTML去除标签逻辑
        String text = html.replaceAll("<[^>]*>", "");
        // 去除多余的空白字符
        text = text.replaceAll("\\s+", " ");
        return text.trim();
    }
    
    /**
     * 处理HTML内容并转换为Word格式，保留基本的格式信息
     * @param htmlContent HTML格式的报告内容
     * @param document Word文档对象
     */
    private void processHtmlContentToWord(String htmlContent, XWPFDocument document) {
        if (htmlContent == null || htmlContent.trim().isEmpty()) {
            return;
        }
        
        // 修复HTML中不规范的标签
        htmlContent = fixBrokenHtmlTags(htmlContent);
        
        // 处理H1标签内容为大写
        htmlContent = processH1Tags(htmlContent);
        
        // 解析HTML内容并转换为Word格式
        // 提取标题
        Pattern h1Pattern = Pattern.compile("<h1>(.*?)</h1>");
        Matcher h1Matcher = h1Pattern.matcher(htmlContent);
        if (h1Matcher.find()) {
            String title = h1Matcher.group(1);
            XWPFParagraph titlePara = document.createParagraph();
            titlePara.setAlignment(ParagraphAlignment.CENTER);
            XWPFRun titleRun = titlePara.createRun();
            titleRun.setText(title);
            titleRun.setBold(true);
            titleRun.setFontSize(16);
        }
        
        // 处理普通段落内容
        Pattern pPattern = Pattern.compile("<p>(.*?)</p>");
        Matcher pMatcher = pPattern.matcher(htmlContent);
        while (pMatcher.find()) {
            String paragraphText = pMatcher.group(1);
            XWPFParagraph paragraph = document.createParagraph();
            XWPFRun run = paragraph.createRun();
            run.setText(paragraphText);
        }
        
        // 处理表格内容
        if (htmlContent.contains("<tr>")) {
            XWPFParagraph tableTitlePara = document.createParagraph();
            XWPFRun tableTitleRun = tableTitlePara.createRun();
            tableTitleRun.setBold(true);
            tableTitleRun.setText("报告清单");
            tableTitleRun.setFontSize(14);
            
            // 提取表格中的行数据
            Pattern trPattern = Pattern.compile("<tr>(.*?)</tr>", Pattern.DOTALL);
            Matcher trMatcher = trPattern.matcher(htmlContent);
            while (trMatcher.find()) {
                String rowContent = trMatcher.group(1);
                // 提取单元格内容（包括未正确闭合的情况）
                Pattern tdPattern = Pattern.compile("<td>(.*?)(</td>|(?=<td>|</tr>))");
                Matcher tdMatcher = tdPattern.matcher(rowContent);
                StringBuilder rowBuilder = new StringBuilder();
                while (tdMatcher.find()) {
                    String cellContent = tdMatcher.group(1);
                    // 清理单元格内容中的特殊字符
                    cellContent = cellContent.replace("\n", "").trim();
                    rowBuilder.append(cellContent).append("\t");
                }
                
                if (rowBuilder.length() > 0) {
                    XWPFParagraph rowPara = document.createParagraph();
                    rowPara.setIndentationLeft(500);
                    XWPFRun rowRun = rowPara.createRun();
                    rowRun.setText(rowBuilder.toString());
                }
            }
        }
        
        // 提取并处理剩余文本内容
        String plainText = extractPlainTextFromHtml(htmlContent);
        // 按合理的方式分割文本
        String[] parts = plainText.split("(?=[\u4e00-\u9fa5])\s{2,}");
        for (String part : parts) {
            if (part.trim().length() > 5) { // 只处理有一定长度的文本
                boolean isAlreadyAdded = false;
                // 检查是否已经添加过类似内容
                for (XWPFParagraph existingPara : document.getParagraphs()) {
                    if (existingPara.getText().contains(part.substring(0, Math.min(10, part.length())))) {
                        isAlreadyAdded = true;
                        break;
                    }
                }
                
                if (!isAlreadyAdded) {
                    XWPFParagraph paragraph = document.createParagraph();
                    XWPFRun run = paragraph.createRun();
                    run.setText(part.trim());
                }
            }
        }
    }
    

    
    private String processH1Tags(String htmlContent) {
        // 使用正则表达式找到所有h1标签内容并转换为大写
        Pattern pattern = Pattern.compile("<h1>(.*?)</h1>", Pattern.DOTALL);
        Matcher matcher = pattern.matcher(htmlContent);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String content = matcher.group(1);
            matcher.appendReplacement(sb, "<h1>" + content.toUpperCase() + "</h1>");
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
    
    private String fixBrokenHtmlTags(String htmlContent) {
        // 修复特定的错误标签 - 从用户提供的示例中看到的错误
        htmlContent = htmlContent.replace("<按照事故对造成的人身损害进行治疗所发生的医疗费用计算", "按照事故对造成的人身损害进行治疗所发生的医疗费用计算");
        
        // 修复未闭合的td标签
        htmlContent = htmlContent.replace("/td>", "</td>");
        
        // 修复缺少table标签的情况
        if (htmlContent.contains("<tr>") && !htmlContent.contains("<table>")) {
            int firstTrIndex = htmlContent.indexOf("<tr>");
            int lastTrIndex = htmlContent.lastIndexOf("</tr>");
            if (firstTrIndex != -1 && lastTrIndex != -1) {
                // 在第一个tr前添加table开始标签
                StringBuilder sb = new StringBuilder(htmlContent);
                sb.insert(firstTrIndex, "<table>");
                // 在最后一个/tr后添加table结束标签
                sb.insert(lastTrIndex + 5, "</table>");
                htmlContent = sb.toString();
            }
        }
        
        return htmlContent;
    }

    @Override
    public String generateHtmlReport(String sessionId, Map<String, Object> data) {
        StringBuilder finalHtml = new StringBuilder();

        // 1. 加载报告布局配置
        ReportLayout layout = reportLayoutMapper.selectList(null).stream()
                .filter(l -> l.getIsActive() == 1)
                .findFirst()
                .orElseGet(() -> createDefaultLayout());

        // 2. 渲染Item级模板
        Map<String, String> renderedItems = renderItemTemplates(data);

        // 3. 通过映射关系组装Section
        Map<String, String> sections = assembleSections(renderedItems);

        // 4. 整合为完整HTML报告
        finalHtml.append("<html><head><meta charset='UTF-8'><title>法律计算报告</title></head><body>");

        // 添加标题
        finalHtml.append("<h1>法律计算报告</h1>");
        finalHtml.append("<p>生成时间: " + new Date() + "</p>");
        finalHtml.append("<p>会话ID: " + sessionId + "</p>");

        // 添加各个章节
        sections.forEach((sectionName, content) -> {
            finalHtml.append("<div class='section'><h2>").append(sectionName).append("</h2>")
                    .append(content).append("</div>");
        });

        finalHtml.append("</body></html>");

        return finalHtml.toString();
    }

    private Map<String, Object> convertAnswersToMap(List<SubmitAnswers> answers) {
        Map<String, Object> userData = new HashMap<>();
        for (SubmitAnswers answer : answers) {
            try {
                StringBuilder mapKey = new StringBuilder();
                String key = null;
                
                // 处理选项ID
                if (answer.getOptionId() != null) {
                    LambdaQueryWrapper<QuestionOptions> eq = new LambdaQueryWrapper<QuestionOptions>()
                            .eq(QuestionOptions::getOId, answer.getOptionId());
                    QuestionOptions questionOptions = questionOptionsMapper.selectOne(eq);
                    if (questionOptions != null) {
                        key = questionOptions.getONumber();
                        mapKey.append(key);
                    }
                } else if (answer.getQuestionId() != null) {
                    // 使用问题ID构建键
                    key = "q" + answer.getQuestionId();
                    mapKey.append(key);
                }
                
                // 获取键名
                String mapKeyStr = mapKey.toString();
                
                if (!mapKeyStr.isEmpty()) {
                    // 处理答案值
                    String answerValue = answer.getAnswer();
                    
                    // 如果答案为null，尝试设置默认值为0（特别是对于数值计算相关的问题）
                    if (answerValue == null) {
                        // 检查问题是否可能是数值类型（这里可以根据问题ID的模式判断）
                        if (key != null && (key.startsWith("q") || isNumericQuestion(key))) {
                            log.warn("Answer value is null for key {}, setting default value 0 for calculation", mapKeyStr);
                            userData.put(mapKeyStr, 0);
                        } else {
                            // 非数值类型问题，设置为空字符串
                            userData.put(mapKeyStr, "");
                        }
                    } else {
                        // 尝试将字符串转换为数字（对于数值计算相关的问题）
                        try {
                            if (key != null && (key.startsWith("q") || isNumericQuestion(key))) {
                                double numValue = Double.parseDouble(answerValue);
                                userData.put(mapKeyStr, numValue);
                            } else {
                                userData.put(mapKeyStr, answerValue);
                            }
                        } catch (NumberFormatException e) {
                            // 不是有效的数字，保持原样
                            userData.put(mapKeyStr, answerValue);
                            log.debug("Non-numeric value '{}' for key {}", answerValue, mapKeyStr);
                        }
                    }
                    
                    // 特别处理地区字段
                    if (answer.isRegion() && answerValue != null) {
                        userData.put("region", answerValue);
                    }
                }
            } catch (Exception e) {
                log.error("Error processing answer: {}", answer, e);
                // 继续处理下一个答案，避免一个错误影响整个处理流程
            }
        }
        return userData;
    }
    
    /**
     * 判断是否为数值类型问题
     * 可以根据实际业务逻辑扩展此方法
     */
    private boolean isNumericQuestion(String key) {
        // 检查是否包含数字，通常数值计算问题会有数字标识
        return key.matches(".*\\d+");
    }

    private Map<String, String> renderItemTemplates(Map<String, Object> data) {
        Map<String, String> renderedItems = new HashMap<>();
        List<ItemTemplate> templates = itemTemplateMapper.selectList(null);

        for (ItemTemplate template : templates) {
            String contentTemplate = template.getContentTemplate();
            if (contentTemplate == null) continue;

            // 使用Aviator渲染模板变量
            String renderedContent = renderTemplateWithAviator(contentTemplate, data);
            renderedItems.put(template.getId().toString(), renderedContent);
        }

        return renderedItems;
    }

    private String renderTemplateWithAviator(String template, Map<String, Object> data) {
        // 简单的模板替换实现，实际可以使用更复杂的模板引擎
        String result = template;
        // 匹配 ${variable} 格式的变量
        java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\$\\{([^\\}]+)\\}");
        java.util.regex.Matcher matcher = pattern.matcher(template);

        while (matcher.find()) {
            String varName = matcher.group(1);
            Object value = data.getOrDefault(varName, "");
            result = result.replace("${" + varName + "}", value.toString());
        }

        return result;
    }

    private Map<String, String> assembleSections(Map<String, String> renderedItems) {
        Map<String, String> sections = new HashMap<>();
        List<ItemSectionMapping> mappings = itemSectionMappingMapper.selectList(null);

        // 按section分组
        Map<String, List<ItemSectionMapping>> sectionGroups = mappings.stream()
                .collect(Collectors.groupingBy(ItemSectionMapping::getSectionName));

        // 组装每个章节的内容
        for (Map.Entry<String, List<ItemSectionMapping>> entry : sectionGroups.entrySet()) {
            StringBuilder sectionContent = new StringBuilder();
            for (ItemSectionMapping mapping : entry.getValue()) {
                String itemContent = renderedItems.get(mapping.getItemId().toString());
                if (itemContent != null) {
                    sectionContent.append(itemContent).append("<br/>");
                }
            }
            sections.put(entry.getKey(), sectionContent.toString());
        }

        return sections;
    }

    /**
     * 保存会话数据
     * @param sessionId 会话ID
     * @param answers 用户答案列表
     * @param calculationResults 计算结果
     * @param htmlContent HTML报告内容
     * @param calculatorCode 计算器编码，由appId决定
     */
    private void saveSessionData(String sessionId, List<SubmitAnswers> answers, Map<String, Object> calculationResults, String htmlContent, String calculatorCode) {
        // 数据验证
        if (sessionId == null || sessionId.trim().isEmpty()) {
            throw new IllegalArgumentException("会话ID不能为空");
        }
        
        if (answers == null) {
            answers = new ArrayList<>();
            log.warn("答案列表为空，使用空列表代替");
        }
        
        if (calculationResults == null) {
            calculationResults = new HashMap<>();
            log.warn("计算结果为空，使用空Map代替");
        }
        
        if (htmlContent == null || htmlContent.trim().isEmpty()) {
            htmlContent = "<html><head><meta charset='UTF-8'><title>法律计算报告</title></head><body><h1>法律计算报告</h1><p>无数据</p></body></html>";
            log.warn("HTML内容为空，使用默认内容代替");
        }
        
        // 如果calculatorCode为空，设置默认值
        if (calculatorCode == null || calculatorCode.trim().isEmpty()) {
            calculatorCode = "DEFAULT_CALCULATOR";
            log.warn("计算器编码为空，使用默认值: {}", calculatorCode);
        }
        
        try {
            ReportSessions session = new ReportSessions();
            session.setSessionId(sessionId);
            // 设置计算器编码，使用传入的calculatorCode（由appId决定）
            session.setCalculatorCode(calculatorCode);
            session.setUserAnswers(answers);
            session.setCalculationResults(calculationResults);
            // 限制HTML内容长度，防止过长
            if (htmlContent.length() > 100000) {
                htmlContent = htmlContent.substring(0, 100000) + "...<p>内容过长，已截断</p></body></html>";
                log.warn("HTML内容过长，已截断");
            }
            session.setHtmlContent(htmlContent);
            session.setCreatedTime(new Date());
            session.setExpiresTime(Date.from(LocalDateTime.now().plusDays(7).toInstant(ZoneOffset.UTC)));

            // 先查询是否存在，避免重复插入
            ReportSessions existing = reportSessionsMapper.selectOne(
                    new LambdaQueryWrapper<ReportSessions>().eq(ReportSessions::getSessionId, sessionId));
            
            if (existing != null) {
                session.setId(existing.getId());
                reportSessionsMapper.updateById(session);
                log.info("更新报告会话: {}", sessionId);
            } else {
                reportSessionsMapper.insert(session);
                log.info("保存新的报告会话: {}", sessionId);
            }
        } catch (Exception e) {
            log.error("保存会话数据失败，会话ID: {}", sessionId, e);
            // 包装异常，提供更明确的错误信息
            throw new RuntimeException("保存报告会话数据失败，请稍后重试", e);
        }
    }

    private void convertHtmlToWord(String html, OutputStream outputStream) throws IOException {
        // 简化实现，实际应该使用POI或Docx4j进行复杂的HTML到Word转换
        XWPFDocument document = new XWPFDocument();
        // 添加一些基本内容
        document.createParagraph().createRun().setText("法律计算报告");
        document.createParagraph().createRun().setText("（HTML转Word简化版）");

        // 将HTML作为文本内容写入（实际应该解析HTML标签）
        String plainText = html.replaceAll("<[^>]*>", " ");
        document.createParagraph().createRun().setText(plainText);

        document.write(outputStream);
        document.close();
    }

    private ReportLayout createDefaultLayout() {
        ReportLayout layout = new ReportLayout();
        layout.setTemplateCode("default");
        layout.setTemplateName("默认报告模板");
        layout.setIsActive(1);
        layout.setCreatedTime(new Date());
        return layout;
    }
}